package com.loong.backend.gateway.filter;


import com.alibaba.fastjson.JSONObject;
import com.loong.backend.gateway.handler.TokenHandler;
import com.loong.common.enumerate.ResultCode;
import com.loong.common.rest.Result;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;


/**
 * Token验证过滤器
 *
 * 全局过滤器，默认处理所有请求的验证
 * 若需要排除验证的API请在filter/exclude.yml中指定
 */
@Slf4j
public class TokenFilter implements GlobalFilter, Ordered {

    /**
     * 排除签名验证的API
     */
    private List<String> tokenExclude;

    /**
     * 路径匹配验证器
     */
    private PathMatcher pathMatcher = new AntPathMatcher();

    private TokenHandler tokenHandler;

    public TokenFilter(List<String> tokenExclude, TokenHandler tokenHandler) {
        this.tokenExclude = tokenExclude;
        this.tokenHandler = tokenHandler;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (CollectionUtils.isEmpty(tokenExclude)) {
            return chain.filter(exchange);
        }
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest request = exchange.getRequest();
        // 获取请求的路径
        String requestPath = request.getURI().getRawPath();
        if (check(requestPath)) {
            return chain.filter(exchange);
        }
        String token = getToken(request);
        String uid = getUid(request);
        if (StringUtils.isEmpty(token) || StringUtils.isEmpty(uid)) {
            return validateFail(response, ResultCode.TOEKNUNVALIBLE, null);
        }
        Integer headerUserId = null;
        try {
             headerUserId = Integer.parseInt(uid);
        }catch (Exception e){
            return validateFail(response, ResultCode.TOEKNUNVALIBLE, null);
        }
        if (tokenHandler.validationToken(token, headerUserId ,requestPath)) {
            // 刷新Token在Redis的存活时间
            tokenHandler.setTokenAndAdminUserInfoTTL(120, token);
            return chain.filter(exchange);
        } else {
            return validateFail(response, ResultCode.TOEKNUNVALIBLE, null);
        }
    }

    @Override
    public int getOrder() {
        return Integer.MIN_VALUE + 1;
    }

    private Mono<Void> validateFail(ServerHttpResponse response, ResultCode code, Object data) {
        Result result = Result.fail(data,code.getMessage(),code);
        String resultString = JSONObject.toJSONString(result);
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        DataBuffer buffer = response.bufferFactory().wrap(resultString.getBytes(StandardCharsets.UTF_8));
        return response.writeWith(Mono.just(buffer));
    }

    private String getUid(ServerHttpRequest request) {
        HttpHeaders httpHeaders = request.getHeaders();
        String uid = httpHeaders.getFirst("uid");
        if (StringUtils.isEmpty(uid)) {
            uid = request.getQueryParams().getFirst("uid");
        }
        return uid;
    }

    private String getToken(ServerHttpRequest request) {
        HttpHeaders httpHeaders = request.getHeaders();
        String token = httpHeaders.getFirst("token");
        if (StringUtils.isEmpty(token)) {
            token = request.getQueryParams().getFirst("token");
        }
        return token;
    }

    private boolean check(final String requestPath) {
        if (tokenExclude.contains(requestPath)) {
            return true;
        }
        for (String excludePath : tokenExclude) {
            if (pathMatcher.match(excludePath, requestPath)) {
                return true;
            }
        }
        return false;
    }

}
